#include <sys/mman.h>
#include <config.h>
#include <display_manager.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
#include <string.h>

static int g_iFdFb;
static struct fb_var_screeninfo g_tVar;
static struct fb_fix_screeninfo g_tFix;
static int g_iLineWidth;
static int g_iPixelWidth;
static unsigned char *g_pucFBMem;
static unsigned int g_dwScreenSize;

static int FBDeviceInit(void);
static int FBShowPixel(int iX, int iY, unsigned int dwColor);
static int FBClearScreen(unsigned int dwColor);


static T_DispOpr g_tFBDispOpr = {
	.name        = "fb",
	.DeviceInit  = FBDeviceInit,
	.ShowPixel   = FBShowPixel,
	.ClearScreen = FBClearScreen,
};

static int FBDeviceInit(void)
{
	g_iFdFb = open(FB_DEVICE_NAME, O_RDWR);
	if(g_iFdFb < 0)
	{
		DEG_PRINTF("Can't open fb0\n");
		return -1;
	}

	if(ioctl(g_iFdFb, FBIOGET_VSCREENINFO, &g_tVar))
	{
		DEG_PRINTF("Can't get var params\n");
		return -1;
	}

	if(ioctl(g_iFdFb, FBIOGET_FSCREENINFO, &g_tFix))
	{
		DEG_PRINTF("Can't get fix params\n");
		return -1;
	}

	g_tFBDispOpr.iXres = g_tVar.xres;
	g_tFBDispOpr.iYres = g_tVar.yres;
	g_tFBDispOpr.iBpp  = g_tVar.bits_per_pixel;
	
	g_iLineWidth  = g_tVar.xres*g_tVar.bits_per_pixel/8;
	g_iPixelWidth = g_tVar.bits_per_pixel/8;
	g_dwScreenSize = g_tVar.xres*g_tVar.yres*g_tVar.bits_per_pixel/8;
	g_pucFBMem    = (unsigned char *)mmap(NULL, g_dwScreenSize, PROT_READ | PROT_WRITE, MAP_SHARED, g_iFdFb, 0);
	if(g_pucFBMem == (unsigned char *)-1)
	{
		DEG_PRINTF("Can't mmap fbmem\n");
		return -1;
	}
	return 0;
}

static int FBShowPixel(int iX, int iY, unsigned int dwColor)
{
	unsigned char *pucPen8 = g_pucFBMem + iY*g_iLineWidth + iX*g_iPixelWidth;
	unsigned short *pwPen16;
	unsigned int *pdwPen32;

	int red, green, blue;

	pwPen16 = (unsigned short *)pucPen8;
	pdwPen32 = (unsigned int *)pucPen8;

	switch(g_tVar.bits_per_pixel)
	{
		case 8:
		{
			*pucPen8 = (unsigned char)dwColor;
			break;
		}

		case 16:
		{
			red = ((dwColor>>16)>>3) & 0x1f;
			green = ((dwColor>>8)>>2) & 0x3f;
			blue = ((dwColor>>0)>>3) & 0x1f;
			dwColor = (red<<11) | (green<<5) | (blue);
			*pwPen16 = dwColor;
			break;
		}

		case 32:
		{
			*pdwPen32 = dwColor;
			break;
		}
	}
	return 0;
}

static int FBClearScreen(unsigned int dwColor)
{
	unsigned char *pucPen8 = g_pucFBMem;
	unsigned short *pwPen16;
	unsigned int *pdwPen32;

	int red, green, blue;
	int i = 0;

	pwPen16 = (unsigned short *)pucPen8;
	pdwPen32 = (unsigned int *)pucPen8;

	switch(g_tVar.bits_per_pixel)
	{
		case 8:
		{
			memset(g_pucFBMem, dwColor, g_dwScreenSize);
			break;
		}

		case 16:
		{
			red = ((dwColor>>16)>>3) & 0x1f;
			green = ((dwColor>>8)>>2) & 0x3f;
			blue = ((dwColor>>0)>>3) & 0x1f;
			dwColor = (red<<11) | (green<<5) | (blue);
			while(i < g_dwScreenSize)
			{
				*pwPen16 = dwColor;
				i += 2;
				pwPen16++;
			}
			break;
		}

		case 32:
		{		
			while(i < g_dwScreenSize)
			{
				*pdwPen32 = dwColor;
				pdwPen32++;
				i += 4;
			}
			break;
		}

	}
	return 0;
}

int FBRegister(void)
{
	return RegisterDispOpr(&g_tFBDispOpr);
}




